﻿using MiniFox.Caching;
using MiniFox.Components;
using MiniFox.Infrastructure;
using MiniFox.Platform.Exceptions;
using MiniFox.Platform.Jwt.Configurations;
using MiniFox.Platform.Jwt.Models;
using MiniFox.Platform.Jwt.Providers;
using MiniFox.Security;

namespace MiniFox.Platform.Jwt.Services
{
    /// <summary>
    /// jwt令牌服务
    /// </summary>
    public class JwtService : Component, IJwtService
    {
        /// <summary>
        /// 
        /// </summary>
        public JwtService()
        {

        }
        /// <summary>
        /// 
        /// </summary>
        [AutoWired]
        protected IApiAuthorizationProvider Provider { get; set; }
        /// <summary>
        /// 
        /// </summary>
        [AutoConfigure]
        protected JwtConfiguration Configuration
        {
            get;
            set;
        }


        #region 管理 json web token

        #region 签名管理

        string GenerateSignature(SignatureAlgorithm algorithm, string tokenString, string signKey)
        {
            string signature = string.Empty;
            switch (algorithm)
            {
                case SignatureAlgorithm.MD5:
                    signature = (tokenString + signKey).MD5Encrypt();
                    break;
                case SignatureAlgorithm.SHA1:
                    signature = (tokenString + signKey).SHA1Encrypt();
                    break;
                case SignatureAlgorithm.SHA256:
                    signature = (tokenString + signKey).SHA256Encrypt();
                    break;
                case SignatureAlgorithm.SHA384:
                    signature = (tokenString + signKey).SHA384Encrypt();
                    break;
                case SignatureAlgorithm.SHA512:
                    signature = (tokenString + signKey).SHA512Encrypt();
                    break;
                case SignatureAlgorithm.HS1:
                    signature = tokenString.HMACSHA1Encrypt(signKey);
                    break;
                case SignatureAlgorithm.HS256:
                    signature = tokenString.HMACSHA256Encrypt(signKey);
                    break;
                case SignatureAlgorithm.HS384:
                    signature = tokenString.HMACSHA384Encrypt(signKey);
                    break;
                case SignatureAlgorithm.HS512:
                    signature = tokenString.HMACSHA512Encrypt(signKey);
                    break;
                case SignatureAlgorithm.DES:
                    signature = tokenString.DesEncrypt(signKey, this.Configuration.DefaultIV);
                    break;
                case SignatureAlgorithm.RC2:
                    signature = tokenString.Rc2Encrypt(signKey, this.Configuration.DefaultIV);
                    break;
                case SignatureAlgorithm.TripleDES:
                    signature = tokenString.TripleDesEncrypt(signKey, this.Configuration.DefaultIV);
                    break;
                case SignatureAlgorithm.AES:
                case SignatureAlgorithm.Rijndael:
                    signature = tokenString.AesEncrypt(signKey, this.Configuration.DefaultIV);
                    break;
                default:
                    signature = (tokenString + signKey).MD5Encrypt();
                    break;
            }
            return signature;
        }
        void GenerateSignature(JsonWebToken accessToken, string signKey)
        {
            accessToken.Signature = this.GenerateSignature(accessToken.Header.Algorithm, accessToken.TokenString, signKey);
        }
        bool VerifySignature(JsonWebToken accessToken, string signKey)
        {
            var signature = this.GenerateSignature(accessToken.Header.Algorithm, accessToken.TokenString, signKey);
            return accessToken.Signature == signature;
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="signatureKey"></param>
        /// <returns></returns>
        protected virtual JsonWebToken CreateJsonWebToken(string appId, string signatureKey)
        {
            var header = new TokenHeader(this.Configuration.SignatureAlgorithm);
            var accessToken = new JsonWebToken(header, appId, this.Configuration.TokenIssuer, this.Configuration.AccessTokenExpires);
            this.GenerateSignature(accessToken, signatureKey);
            this.Provider.SaveAccessToken(accessToken);

            return accessToken;
        }
        /// <summary>
        /// 接入方获取访问令牌
        /// </summary>
        /// <param name="appId">接入方应用编号</param>
        /// <param name="secretKey">接入方安全码</param>
        /// <returns></returns>
        public virtual string GetJsonWebToken(string appId, string secretKey)
        {
            AppSecret appSecret = this.Provider.GetAppSecret(appId);
            if (appSecret == null || appSecret.SecretKey != secretKey)
            {
                throw new PlatformException("ERR_UnknownClientApp");
            }
            string cacheKey = appId;

            var jwt = CacheService.GetCacheObject(cacheKey, () =>
            {
                JsonWebToken token = this.Provider.LoadAccessToken(appId);
                if (token == null)
                {
                    token = CreateJsonWebToken(appId, appSecret.SignatureKey);
                }
                else
                {
                    this.GenerateSignature(token, appSecret.SignatureKey);
                }
                return token.ToString();
            }, this.Configuration.CacheDuration);
            return jwt;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jwt"></param>
        /// <returns></returns>
        public Response VerifyJsonWebToken(string jwt)
        {
            Response response = new Response();
            try
            {
                JsonWebToken token = jwt;
                this.VerifyJsonWebToken(token);
            }
            catch (PlatformException e)
            {
                response.Message = e.Message;
            }

            return response;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        protected virtual void VerifyJsonWebToken(JsonWebToken accessToken)
        {
            if (accessToken == null)
            {
                throw new PlatformException("ERR_InvalidAccessToken", accessToken.JsonValue);
            }
            if (accessToken.IsExpired())
            {
                throw new PlatformException("ERR_AccessTokenExpired", accessToken.JsonValue);
            }
            string appId = accessToken.Payload.ApplicationID;
            AppSecret appSecret = this.Provider.GetAppSecret(appId);
            if (appSecret == null)
            {
                throw new PlatformException("ERR_UnknownApiClient", appId);
            }

            if (this.VerifySignature(accessToken, appSecret.SignatureKey))//验签
            {
                throw new PlatformException("ERR_AccessTokenSignFailed", accessToken.JsonValue);
            }

        }

        #endregion


        /// <summary>
        /// 
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public virtual ApplicationIdentity GetIdentity(JsonWebToken accessToken)
        {
            if (accessToken == null)
            {
                throw new PlatformException("ERR_InvalidAccessToken", accessToken.JsonValue);
            }
            if (accessToken.IsExpired())
            {
                throw new PlatformException("ERR_AccessTokenExpired", accessToken.JsonValue);
            }
            string appId = accessToken.Payload.ApplicationID;
            AppSecret appSecret = this.Provider.GetAppSecret(appId);
            if (appSecret == null)
            {
                throw new PlatformException("ERR_UnknownApiClient", appId);
            }

            if (this.VerifySignature(accessToken, appSecret.SignatureKey))//验签
            {
                throw new PlatformException("ERR_AccessTokenSignFailed", accessToken.JsonValue);
            }

            return this.Provider.GetApplicationIdentity(appId);
        }

    }
}
